package pathFinderPackage.BiDijkstra;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import mainPackage.PathFinderListener;
import mapPackage.IDTroncon;
import mapPackage.ITroncon;
import mapPackage.IVille;
import mapPackage.MapException;
import pathFinderPackage.IChercheurItineraire;
import pathFinderPackage.Itineraire;
import preferencePackage.Preference;
import xmlPackage.Carte;

public class BiDirectionnalDijkstra extends IChercheurItineraire 
{
	
	
	private String villeD ;
	private String villeA ;
	
	private static ThreadBiDijkstra tF = null ;
	private static ThreadBiDijkstra tB = null ;

	private Itineraire demi1 =null;
	private Itineraire demi2 =null;
	
	
	
	private List<Preference> preferences ; 
	
	private boolean pasdesolution;
	

	public BiDirectionnalDijkstra( )
	{
		super();
		
		pasdesolution = false;
		
		tF = null ;
		tB = null ;

		demi1 =null;
		demi2 =null;
	}
	
	public void trouverItineraire(String villeDepart , String villeArrivee , List<Preference> preferences  ,  HashMap<String,Boolean> villesIntermediaires ) throws MapException
	{
		//System.out.println("Recherche entre "+villeDepart+" et "+villeArrivee);
		//if(tF != null && tB != null)System.out.println("etats des 2 threads avt lancement : "+tB.getState()+" et "+tF.getState());
		
		ThreadBiDijkstra.RAZ();
		demi1= null ;
		demi2= null ;
		m_itenaire=null;
		
		pasdesolution = false;
		
		this.villeD = villeDepart;
		this.villeA = villeArrivee;
		this.preferences = preferences;
		
		
		tF = new ThreadBiDijkstra("F" , villeDepart , villeArrivee , preferences , this , villesIntermediaires);
		
		tB = new ThreadBiDijkstra("B" , villeArrivee , villeDepart , preferences , this, villesIntermediaires );
		
		tF.start();
		tB.start();
		
		
		//if(tF != null && tB != null)System.out.println("etats des 2 threads avt attente : "+tB.getState()+" et "+tF.getState());
		if( tF != null || tB != null  )
		{

			try 
			{
					//System.out.print("attente de TB meurt");
					tB.join();
					//System.out.println(" TB mort");	
				
			} catch (InterruptedException e) {	e.printStackTrace();}
			
			try 
			{

					//System.out.print("attente de TF meurt");
					tF.join();
					//System.out.println(" TF mort");
				
			} catch (InterruptedException e) {	e.printStackTrace();}

		}
		/*
		try 
		{
			if(tF != null && tB != null)System.out.println("etats des 2 threads avt wait : "+tB.getState()+" et "+tF.getState());
			synchronized(waiting){waiting.wait();}
			
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		*/
		
		//System.out.println("iti="+this.bestIti);
		//if(tF != null && tB != null)System.out.println("etats des 2 threads apres lancement : "+tB.getState()+" et "+tF.getState());
		
		
	}
	
	public synchronized void demieChemin( Itineraire it , String quelmoitie )  
	{
		
		if( quelmoitie.equals("F") )
		{
			this.demi2 = it ;
			//System.out.println("\ndemi chemin 2 ok entre "+it.getVilleDepart()+" et "+it.getVilleArrivee()+" : " );
		}
		if( quelmoitie.equals("B") )
		{
			this.demi1 = it ;
			//System.out.println("\ndemi chemin 1 ok entre "+it.getVilleDepart()+" et "+it.getVilleArrivee()+" : " );
		}
		
		if( demi2!=null && demi1!=null )
		{
			
			//System.out.println("iti = "+this.demi2.getVilleDepart()+" - "+this.demi2.getVilleArrivee()+" - "+this.demi1.getVilleArrivee()+" - "+this.demi1.getVilleDepart());	
			this.demi1.ItineraireRetour();
			
			
			try 
			{
				this.demi1.fusionnerAvec(demi2);
			}
			catch (Exception e) {e.printStackTrace();}
			
			
			
			if( this.m_itenaire == null )
			{
				this.m_itenaire = this.demi1 ;
				
				fireUneSolutionTrouvee(this.demi1);
			}
			else
			{
				if( evaluerSolution(this.m_itenaire) < evaluerSolution(this.demi1) )
				{
					this.m_itenaire = this.demi1;
					//fireMeilleureSolutionTrouvee(this.bestIti);
				}
			}
			
			
		}
	}
	
	
	private float evaluerSolution( Itineraire solution )
	{
		try 
		{
			float score = 0 ;
			IVille ville = Carte.GetInstance().getVille( solution.getVilleDepart() ) ;
			for( IDTroncon ts :  solution.getTroncons() )
			{
				ITroncon to = Carte.GetInstance().getTroncon(ts) ;
				
				// on evalue selon toutes les preferences :
				for( Preference p : this.preferences )
				{
					score += ( p.evaluerDestination(ville, to) * p.getClassement() ) ;
				}
				
			}
			
			return score;
		}
		catch (MapException e) {e.printStackTrace();}
		return 0;
	}
	
	public void cheminDijkstraSimple( Itineraire it ,  String quelmoitie )  
	{
		/*
		//if(bestIti==null)return;
		if( quelmoitie.equals("B") ){it.ItineraireRetour();}
		
		//System.out.println("score de demie chemin=" + this.evaluerSolution(this.bestIti));
		//System.out.println("score de chemin eniter="+this.evaluerSolution(it));
		
		if( this.bestIti == null )
		{
			this.bestIti = it ;
		
			fireUneSolutionTrouvee(this.bestIti);
		}
		else
		{
			if( evaluerSolution(this.bestIti) < evaluerSolution( it ) )
			{
				this.bestIti = this.demi1;
				fireMeilleureSolutionTrouvee(this.bestIti);
			}
		}
		*/
	}

	
	public void stopperLaRecherche() 
	{
		if( tB != null ) tB.stopperRecherche() ;
		if( tF != null ) tF.stopperRecherche() ;
	}
	
	
	private  void fireUneSolutionTrouvee(Itineraire solution)
	{
		//System.out.println("BID a une solution");
		for( PathFinderListener pfl : this.listeners )
		{
			pfl.SolutionTrouvee(solution);
 
		}
	}
	
	private  void fireMeilleureSolutionTrouvee(Itineraire solution)
	{
	
		//System.out.println("BID a une MEILLEUR solution");
		for( PathFinderListener pfl : this.listeners )
		{
			pfl.MeilleureSolutionTrouvee(solution);
		}
		
	}

	public void ajouterPathFinderListener(PathFinderListener ecouteur) 
	{
		if( ! this.listeners.contains(ecouteur))this.listeners.add(ecouteur);
		
	}

	public void enleverPathFinderListener(PathFinderListener ecouteur) 
	{
		if( this.listeners.contains(ecouteur))this.listeners.remove(ecouteur);
	}


	public synchronized void pasDeSolution(String vD, String vA , String pourquoi)
	{
		
		// on utilise un boolean qui sert a a ne pas faire 2 appels ( car les 2 threads declenchent le signal )
		//System.out.println("ok pbm recu par le BID");
		if( !pasdesolution  )
		{
			System.err.println("BID("+this.villeD+","+this.villeA+") recoit que "+vD+" -> "+vA+"  fuck pasque :"+pourquoi);
			pasdesolution = true ;
			
			/*
			synchronized (waiting) {
				waiting.notify();	
			}
			*/
			
			firePasDeSolutionTrouvee(villeD,villeA,pourquoi);
			
		}

	}
	
	private  void firePasDeSolutionTrouvee(String VD , String VA ,String pourquoi)
	{
		for( PathFinderListener pfl : this.listeners )
		{
			pfl.pasDeSolution(VD, VA,pourquoi);
		}
		
	}
	
}
